package com.youlai.boot.bmp.service.impl;


import com.aizuda.bpm.engine.FlowLongEngine;
import com.aizuda.bpm.engine.core.FlowCreator;
import com.aizuda.bpm.engine.core.enums.FlowState;
import com.aizuda.bpm.engine.core.enums.TaskType;
import com.aizuda.bpm.engine.entity.FlwHisInstance;
import com.aizuda.bpm.engine.entity.FlwInstance;
import com.aizuda.bpm.engine.entity.FlwProcess;
import com.aizuda.bpm.engine.exception.FlowLongException;
import com.aizuda.bpm.engine.model.ModelHelper;
import com.aizuda.bpm.engine.model.NodeAssignee;
import com.aizuda.bpm.engine.model.NodeModel;
import com.aizuda.bpm.mybatisplus.mapper.FlwHisInstanceMapper;
import com.aizuda.bpm.mybatisplus.mapper.FlwProcessMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.youlai.boot.bmp.enums.NodeProcessAssigneeType;
import com.youlai.boot.bmp.model.entity.BizProcessCategoryBinding;
import com.youlai.boot.bmp.model.entity.FormWorkflowBinding;
import com.youlai.boot.bmp.model.entity.ProcessCategory;
import com.youlai.boot.bmp.model.form.BizProcessForm;
import com.youlai.boot.bmp.model.form.InstanceForm;
import com.youlai.boot.bmp.model.query.BizInstanceQuery;
import com.youlai.boot.bmp.model.query.BizProcessCategoryQuery;
import com.youlai.boot.bmp.model.query.FlwProcessQuery;
import com.youlai.boot.bmp.model.vo.BizProcessCategoryListVo;
import com.youlai.boot.bmp.model.vo.BizProcessVo;
import com.youlai.boot.bmp.service.BizProcessCategoryBindingService;
import com.youlai.boot.bmp.service.BizProcessService;
import com.youlai.boot.bmp.service.FormWorkflowBindingService;
import com.youlai.boot.bmp.service.ProcessCategoryService;
import com.youlai.boot.common.util.DistributedBusinessNoGenerator;
import com.youlai.boot.form.enums.DyFormType;
import com.youlai.boot.form.model.form.FormDefinitionForm;
import com.youlai.boot.form.model.vo.FormDefinitionVO;
import com.youlai.boot.form.service.FormDefinitionService;
import com.youlai.boot.system.model.form.UserForm;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Consumer;

@Slf4j
@Service
@AllArgsConstructor
public class BizProcessServiceImpl implements BizProcessService {

    private final FlowLongEngine flowLongEngine;
    private final FlwProcessMapper flwProcessMapper;
    private final FlwHisInstanceMapper flwHisInstanceMapper;
    private final FormDefinitionService formDefinitionService;
    private final FormWorkflowBindingService formWorkflowBindingService;
    private final BizProcessCategoryBindingService bizProcessCategoryBindingService;
    private final ProcessCategoryService processCategoryService;


    @Override
    public List<FlwProcess> getProcessList() {
        return flwProcessMapper.selectList(null);

    }

    @Override
    public BizProcessVo getBizProcessById(Long id) {
        return formWorkflowBindingService.getBizProcessVoById(id);
    }

    @Override
    public List<BizProcessVo> getBizProcessList(List<Long> ids) {
        if (CollectionUtils.isNotEmpty(ids)){
            FlwProcessQuery query = new FlwProcessQuery();
            query.setIds(ids);
            return formWorkflowBindingService.getBizProcessVoList(query);
        }
        return List.of();
    }

    @Override
    public Page<BizProcessCategoryListVo> getBizProcessCategoryList(BizProcessCategoryQuery queryParams) {
        //先分页获取分类列表

        Page<ProcessCategory> page = processCategoryService.page(new Page<>(queryParams.getPageNum(), queryParams.getPageSize()), new LambdaQueryWrapper<ProcessCategory>()
                .eq(Objects.nonNull(queryParams.getCategoryId()), ProcessCategory::getId, queryParams.getCategoryId())
                .like(!StringUtils.isBlank(queryParams.getCategoryName()), ProcessCategory::getCategoryName, queryParams.getCategoryName()).orderByDesc(ProcessCategory::getSort, ProcessCategory::getCreateTime));


        //然后获取分页列表下的数据，这部分现阶段不分页
        if (page.getTotal() < 1){
            return new Page<>();
        }
        List<BizProcessCategoryListVo> listVo = new ArrayList<>();
        for (ProcessCategory itemCategory : page.getRecords()) {
            BizProcessCategoryListVo itemVo = new BizProcessCategoryListVo();
            itemVo.setProcessCategory(itemCategory);
            List<BizProcessCategoryBinding> categoryBindingList = bizProcessCategoryBindingService.list(new LambdaQueryWrapper<BizProcessCategoryBinding>().select(BizProcessCategoryBinding::getWorkflowKey).eq(BizProcessCategoryBinding::getCategoryId, itemCategory.getId()));
            if (CollectionUtils.isNotEmpty(categoryBindingList)){
                List<String> workflowKeys = categoryBindingList.stream()
                        .map(BizProcessCategoryBinding::getWorkflowKey)
                        .toList();
                FlwProcessQuery query = new FlwProcessQuery();
                query.setWorkflowKeys(workflowKeys);
                query.setProcessName(queryParams.getProcessName());
                //todo 此处配置，超级管理员可以看到所有流程   发起人只能看到启用的流程
                query.setProcessStates(List.of(FlowState.active.getValue(), FlowState.inactive.getValue()));
                List<BizProcessVo> bizProcessVoList = formWorkflowBindingService.getBizProcessVoList(query);
                itemVo.setProcessList(bizProcessVoList);
            }
            listVo.add(itemVo);
        }
        Page<BizProcessCategoryListVo> pageVo = new Page<>();
        pageVo.setCurrent(page.getCurrent());
        pageVo.setSize(page.getSize());
        pageVo.setTotal(page.getTotal());
        pageVo.setRecords(listVo);
        return pageVo;
    }

    @Override
    public boolean switchProcessStatus(Long processId, Integer status) {
        BizProcessVo dbBizProcessVo = getBizProcessById(processId);
        if (Objects.isNull(dbBizProcessVo)){
            throw new FlowLongException("流程不存在");
        }
        boolean processFlag = false;
        if (status == 1){
            //禁用
            processFlag = flowLongEngine.processService().undeploy(processId);
            if (processFlag && Objects.nonNull(dbBizProcessVo.getFormId())){
                boolean fromFlag = formDefinitionService.updateFormDefinitionStatus(dbBizProcessVo.getFormId(), 1);
                if (!fromFlag){
                    throw new FlowLongException("流程禁用（卸载）失败");
                }
            }
        }else {
            //启用
            FlwProcess process = new FlwProcess();
            process.setId(processId);
            process.setFlowState(FlowState.active);
            processFlag = flwProcessMapper.updateById(process) > 0;
            if (Objects.nonNull(dbBizProcessVo.getFormId())){
                boolean  fromFlag = formDefinitionService.updateFormDefinitionStatus(dbBizProcessVo.getFormId(), 0);
                if (!fromFlag){
                    throw new FlowLongException("流程启用(装载)失败");
                }
            }
        }
        return processFlag;
    }

    @Override
    public boolean startInstance(InstanceForm instanceForm, Long userId) {
        //todo 从系统获取当前用户信息 ,
        //获取用户信息
        UserForm userForm = new UserForm();
        userForm.setId(userId);
        userForm.setUsername("admin");
        userForm.setNickname("管理员");
        userForm.setDeptId(1L);
        userForm.setRoleIds(List.of(1L,2L,3L));


        FlowCreator creator = FlowCreator.of(String.valueOf(userId), "admin");
        //todo 鉴定发起人是否有权限发起流程
        FlwProcess dbFlwProcess = flowLongEngine.processService().getProcessById(instanceForm.getProcessId());
        if (Objects.isNull(dbFlwProcess)){
            log.error("流程不存在 - [processId: {}]", instanceForm.getProcessId());
            throw new FlowLongException("流程不存在");
        }
        // 验证发起人权限
        NodeModel nodeConfig = dbFlwProcess.model().getNodeConfig();

        if (!hasPermissionStart(userForm, nodeConfig)){
            return false;
        }
        //todo 需要校验表单参数
        Optional<FlwInstance> flwInstance = flowLongEngine.startInstanceById(instanceForm.getProcessId(), creator, instanceForm.getInstanceParams(),true,()->{
            FlwInstance instance = FlwInstance.of(instanceForm.getHisInstanceKey());
            //分布式业务编号
            instance.setInstanceNo(DistributedBusinessNoGenerator.generate(String.valueOf(userId)));
            return instance;
        });
        return flwInstance.isPresent();
    }

    @Override
    public Page<FlwHisInstance> getInstanceList(Long userId, BizInstanceQuery instanceQuery) {
        Set<Long> processIds = buildProcessIdFilter(instanceQuery);
        // 查询对应的实例ID
        LambdaQueryWrapper<FlwHisInstance> flwQuery = new LambdaQueryWrapper<>();
        flwQuery.eq(Objects.nonNull(userId), FlwHisInstance::getCreateId, userId)
                .eq(Objects.nonNull(instanceQuery.getInstanceState()), FlwHisInstance::getInstanceState, instanceQuery.getInstanceState())
                .in(CollectionUtils.isNotEmpty(processIds),FlwHisInstance::getProcessId, processIds);
        return flwHisInstanceMapper.selectPage(
                new Page<>(instanceQuery.getPageNum(), instanceQuery.getPageSize()),
                flwQuery);
    }


    /**
     * 获取流程列表
     * @param instanceQuery 查询参数
     * @return
     */
    private Set<Long> buildProcessIdFilter(BizInstanceQuery instanceQuery) {
        Set<Long> processIds = new LinkedHashSet<>();

        if (instanceQuery.getProcessCategoryId() != null){
            List<BizProcessCategoryBinding> list = bizProcessCategoryBindingService.list(new LambdaQueryWrapper<BizProcessCategoryBinding>().select(BizProcessCategoryBinding::getWorkflowKey).eq(BizProcessCategoryBinding::getCategoryId, instanceQuery.getProcessCategoryId()));
            if (CollectionUtils.isNotEmpty(list)){
                List<String> workflowKeys = list.stream()
                        .map(BizProcessCategoryBinding::getWorkflowKey)
                        .toList();
                List<FlwProcess> flwProcesses = flwProcessMapper.selectList(new LambdaQueryWrapper<FlwProcess>().select(FlwProcess::getId).in(FlwProcess::getProcessKey, workflowKeys));
                if (CollectionUtils.isNotEmpty(flwProcesses)){
                    flwProcesses.forEach(flwProcess -> processIds.add(flwProcess.getId()));
                }
            }

        }

        if (StringUtils.isNotEmpty(instanceQuery.getProcessName())){
            List<FlwProcess> flwProcesses = flwProcessMapper.selectList(new LambdaQueryWrapper<FlwProcess>().select(FlwProcess::getId).in(FlwProcess::getProcessName, instanceQuery.getProcessName()));
            if (CollectionUtils.isNotEmpty(flwProcesses)){
                flwProcesses.forEach(flwProcess -> processIds.add(flwProcess.getId()));
            }
        }

        return processIds;
    }

    /**
     *  判断当前用户是否有权限发起流程
     *
     * @return true:有权限  false:无权限
     */
    private boolean hasPermissionStart(UserForm userForm, NodeModel nodeModel){
        //todo 获取当前用户信息
        //1.如果是超级管理员，直接放行

        if (nodeModel == null){
            return false ;
        }
        //查找发起人节点
        List<NodeModel> rootNodeAllChildNodes = ModelHelper.getRootNodeAllChildNodes(nodeModel);
        System.out.println(rootNodeAllChildNodes.size());


        NodeModel startNode = null;

        for (NodeModel nodeItem : rootNodeAllChildNodes) {
            if (TaskType.major.eq(nodeItem.getType())){
                startNode = nodeItem;
                break;
            }
        }
        //无开始节点直接不允许发起流程
        if (Objects.isNull(startNode)){
            return false;
        }
        List<NodeAssignee> nodeAssigneeList = startNode.getNodeAssigneeList();
        if (CollectionUtils.isEmpty(nodeAssigneeList)){
            return true;
        }

        for (NodeAssignee nodeAssignee : nodeAssigneeList) {
            //根据NodeAssignee type 和值判断 userId是否满足条件
           Long nodeAssigneeId = Long.valueOf(nodeAssignee.getId());
           if (NodeProcessAssigneeType.USER.eq(nodeAssignee.getType()) && nodeAssignee.getId().equalsIgnoreCase(String.valueOf(userForm.getId()))){
                //todo 根据NodeAssignee value 判断 userId是否满足条件
               return true;
           }
           if (NodeProcessAssigneeType.ROLE.eq(nodeAssignee.getType())){
                //查询用户的角色是否在角色列表中
               if (userForm.getRoleIds().contains(nodeAssigneeId)){
                   return true;
               }
           }
           if (NodeProcessAssigneeType.DEPARTMENT.eq(nodeAssignee.getType())){
               //查询用户的部门是否在列表中
               if (userForm.getDeptId().equals(nodeAssigneeId)){
                   return true;
               }
           }
        }

        return false;
    }

    @Override
    public Long deployBizProcess(BizProcessForm form) {
        //todo 需要校验模型是否满足要求（未实现）
        //此处处理其他的业务逻辑
        FlowCreator creator = FlowCreator.of(form.getCreateId(), form.getCreateBy());
        //todo 是否开启多版本部署（需要从系统中获取参数）
        boolean repeat = true;
        Long deployId = flowLongEngine.processService().deploy(form.getProcessId(), form.getJsonModel(), creator, repeat, new Consumer<FlwProcess>() {
            @Override
            public void accept(FlwProcess flwProcess) {
                //todo 提交成功后更新一下相关字段附加字段
                flwProcess.setProcessIcon(form.getProcessIcon());
                flwProcess.setProcessType(form.getProcessType());
                //todo 获取实例地址
//                flwProcess.setInstanceUrl(form.getInstanceUrl());
                flwProcess.setSort(form.getSort());
                flwProcess.setRemark(form.getRemark());
                //todo 继续执行其他的逻辑
            }
        });
        FlwProcess dbFlwProcess = flowLongEngine.processService().getProcessById(deployId);
        //获取id 后绑定到业务代码中
        // 表单处置关联
        FormDefinitionForm formData = new FormDefinitionForm();
        formData.setFormCode("flw_form_"+dbFlwProcess.getProcessKey());
        formData.setFormName(form.getProcessName()+"-表单");
        formData.setFormType(DyFormType.WORKFLOW.getValue());
        formData.setFormSchema(form.getFormSchema());
        Long formId = formDefinitionService.saveOrUpdateFormDefinition(formData);
        FormDefinitionVO dbFormDefinitionVO = formDefinitionService.getFormDefinitionFormData(formId);
        FormWorkflowBinding formWorkflowBinding = new FormWorkflowBinding();
        formWorkflowBinding.setFormId(formId);
        formWorkflowBinding.setProcessId(dbFlwProcess.getId());
        formWorkflowBinding.setFormCode(dbFormDefinitionVO.getFormCode());
        formWorkflowBinding.setFormVersion(dbFormDefinitionVO.getVersion());
        formWorkflowBinding.setWorkflowKey(dbFlwProcess.getProcessKey());
        formWorkflowBinding.setWorkflowVersion(dbFlwProcess.getProcessVersion());
        formWorkflowBinding.setBindingType(dbFlwProcess.getProcessType());
        formWorkflowBinding.setCreateBy(Long.valueOf(form.getCreateId()));
        formWorkflowBindingService.save(formWorkflowBinding);
        // 分类处置
        Long categoryId = form.getCategoryId();
        ProcessCategory dbProcessCategory = processCategoryService.getById(categoryId);
        if (Objects.isNull(dbProcessCategory)){
            throw new FlowLongException("流程分类不存在");
        }
        BizProcessCategoryBinding bizProcessCategoryBinding = new BizProcessCategoryBinding();
        bizProcessCategoryBinding.setCategoryId(categoryId);
        bizProcessCategoryBinding.setWorkflowKey(dbFlwProcess.getProcessKey());
        // 一个工作流归属绑定一个分类
        boolean exists = bizProcessCategoryBindingService.exists(new LambdaQueryWrapper<BizProcessCategoryBinding>().eq(BizProcessCategoryBinding::getCategoryId, categoryId).eq(BizProcessCategoryBinding::getWorkflowKey, dbFlwProcess.getProcessKey()));
        if (!exists){
            bizProcessCategoryBindingService.save(bizProcessCategoryBinding);
        }
        return deployId;
    }



}
